#pragma once

#include<BWAPI.h>
#include<BWTA.h>
#include<vector>
#include<map>

#include"Command.h"
#include"Unit.h"
#include"Location.h"
#include"MoveList.h"
#include"Calculation.h"
#include"State.h"

using std::vector;
using std::map;
using namespace BWAPI;

class Micro : public AIModule
{
public:
	Micro()
		:_started(false)
	{

	}
	virtual void onStart()
	{
		//Broodwar->setLocalSpeed(0);
		Broodwar->enableFlag(Flag::UserInput);
		Broodwar->enableFlag(Flag::CompleteMapInformation);
		_started = true;
	}
	virtual void onFrame()
	{
		if(_started)
		{
			_myRealUnits.clear();
			_enemyRealUnits.clear();

			for each(BWAPI::Unit* whichUnit in Broodwar->getAllUnits())
			{
				if(whichUnit->getPlayer() == Broodwar->self())
				{
					_myRealUnits.push_back(whichUnit);
					Broodwar->drawLineMap(whichUnit->getPosition().x() , whichUnit->getPosition().y() , whichUnit->getOrderTargetPosition().x() , whichUnit->getOrderTargetPosition().y() , Colors::Green);
				}
				else
				{
					_enemyRealUnits.push_back(whichUnit);
				}
			}

			Broodwar->drawTextScreen( 2 , 2 , "My unit count: %d" , _myRealUnits.size());
			Broodwar->drawTextScreen( 2 , 10 , "Enemy unit count: %d" , _enemyRealUnits.size());

			if(_myRealUnits.size() > 0 && _enemyRealUnits.size() > 0)
			{
				vector<ControlCommand> move(AlphaBeta(40));

				for(int whichUnit = 0 ; whichUnit < _myRealUnits.size() ; ++whichUnit)
				{
					if(move[whichUnit].Type == ControlCommand::Attack)
					{
						SmartAttackUnit(_myRealUnits[whichUnit] , move[whichUnit].Target);
					}
					else if(move[whichUnit].Type == ControlCommand::Move)
					{
						_myRealUnits[whichUnit]->move(Position(move[whichUnit].TargetPosition.X , move[whichUnit].TargetPosition.Y));
					}
				}
				Broodwar->drawTextScreen( 2 , 18 , "Evaluate count: %d" , _evaluateCount);
			}
		}
	}
	virtual void onUnitMorph(Unit* unit)
	{
		Broodwar->sendText("\x17 onUnitMorph: %s" , unit->getType().getName().c_str());
	}
	virtual void onUnitCreate(Unit* unit)
	{
		//Broodwar->sendText("\x08 onUnitCreate: %s" , unit->getType().getName().c_str());
	}
	virtual void onUnitShow(Unit* unit)
	{
		//Broodwar->sendText("\x08 onUnitShow: %s" , unit->getType().getName().c_str());
	}
	//--
	static const int MIN_SCORE = -10000;
	static const int MAX_SCORE = 10000;
private:
	bool _started;
	vector<BWAPI::Unit*> _myRealUnits;
	vector<BWAPI::Unit*> _enemyRealUnits;
	State _state;
	int _evaluateCount;
	//--
	vector<ControlCommand> AlphaBeta(int depth)
	{
		_evaluateCount = 0;
		int alpha = MIN_SCORE;
		int beta = MAX_SCORE;
		Move bestMove;
		State originalState;
		bool initialized = false;
		//--
		_state.Clear();
		_state.MyGroup.RealToGroup(_myRealUnits);
		_state.EnemyGroup.RealToGroup(_enemyRealUnits);

		MoveList movelist(_state.MyGroup , _state.EnemyGroup);

		originalState = _state;

		while(true)
		{
			bool endFlag = false;
			Move moveNow = movelist.GetNextMove(endFlag);
			if(!initialized)
			{
				initialized = true;
				bestMove = moveNow;
			}
			if(endFlag)
			{
				break;
			}

			//Do move
			DoMove(_state.MyGroup , moveNow);

			int score = Min(depth , alpha , beta);

			_state = originalState;

			if(score > alpha)
			{
				alpha = score;
				bestMove = moveNow;
			}
		}
		vector<ControlCommand> answer;
		for(int squadIndex = 0 ; squadIndex < _state.MyGroup.Squads.size() ; ++squadIndex)
		{
			Squad& whichSquad = _state.MyGroup.Squads[squadIndex];
			if(whichSquad.AllDeadOrNoUnit() || bestMove.commands[squadIndex].Type == Command::Skip)
			{
			}
			else
			{
				for(int whichUnitIndex = 0 ; whichUnitIndex < whichSquad.UnitPointers.size() ; ++whichUnitIndex)
				{
					Custom::Unit& whichUnit = *(whichSquad.UnitPointers[whichUnitIndex]);
					whichUnit.CurrentCommand = bestMove.commands[squadIndex];
				}
			}
		}
		for(int whichUnitIndex = 0 ; whichUnitIndex < _state.MyGroup.Units.size() ; ++whichUnitIndex)
		{
			ControlCommand cc;
			Custom::Unit& whichUnit = _state.MyGroup.Units[whichUnitIndex];

			if(whichUnit.CurrentCommand.Type == Command::Attack)
			{
				cc.Type = ControlCommand::Attack;
				cc.Target = whichUnit.CurrentCommand.TargetUnit->RealUnitPointer;
			}
			else if(whichUnit.CurrentCommand.Type == Command::Move)
			{
				cc.Type = ControlCommand::Move;
				cc.TargetPosition = whichUnit.CurrentCommand.TargetPosition;
			}

			answer.push_back(ControlCommand(cc));
		}
		return answer;
	}
	int Max(const int& depth , int alpha , int beta)
	{
		int maxNow = MIN_SCORE;
		State originalState = _state;
		//--

		if(_state.GotWinner())
		{
			return Evaluate();
		}

		MoveList movelist(_state.MyGroup , _state.EnemyGroup);

		while(true)
		{
			bool endFlag = false;
			Move moveNow = movelist.GetNextMove(endFlag);
			if(endFlag)
			{
				break;
			}

			DoMove(_state.MyGroup , moveNow);

			const int score = Tick(depth , alpha , beta);
			if(score > maxNow)
			{
				maxNow = score;
			}
			_state = originalState;
			if(maxNow >= beta)
			{
				return maxNow;
			}
			if(maxNow > alpha)
			{
				alpha = maxNow;
			}
		}
		return maxNow;
	}
	int Min(const int& depth , int alpha , int beta)
	{
		int minNow = MAX_SCORE;
		State originalState = _state;
		//--

		if(_state.GotWinner())
		{
			return Evaluate();
		}

		MoveList movelist(_state.EnemyGroup , _state.MyGroup);

		while(true)
		{
			bool endFlag = false;
			Move moveNow = movelist.GetNextMove(endFlag);
			if(endFlag)
			{
				break;
			}

			DoMove(_state.EnemyGroup , moveNow);

			const int score = Tick(depth , alpha , beta);
			if(score < minNow)
			{
				minNow = score;
			}
			_state = originalState;
			if(minNow <= alpha)
			{
				return minNow;
			}
			if(minNow < beta)
			{
				beta = minNow;
			}
		}
		return minNow;
	}
	void DoMove(Group& movingGroup , const Move& move)
	{
		movingGroup.Target = move.Target;
		for(int squadIndex = 0 ; squadIndex < movingGroup.Squads.size() && squadIndex < move.commands.size() ; ++squadIndex)
		{
			if(move.commands[squadIndex].Type != Command::Skip)
			{
				Squad& whichSquad = movingGroup.Squads[squadIndex];
				for(int unitIndex = 0 ; unitIndex < whichSquad.UnitPointers.size() ; ++unitIndex)
				{
					Custom::Unit& whichUnit = *(whichSquad.UnitPointers[unitIndex]);
					if(whichUnit.Alive())
					{
						whichUnit.IssueCommand(move.commands[squadIndex]);
					}
				}
			}
		}
	}
	int Tick(int depth , int alpha , int beta)
	{
		while(true)
		{
			--depth;
			if(depth <= 0)
			{
				return Evaluate();
			}

			bool maxNeedBranch = false;
			bool minNeedBranch = false;
			for(int whichSquad = 0 ; whichSquad < _state.MyGroup.Squads.size() ; ++whichSquad)
			{
				_state.MyGroup.Squads[whichSquad].Tick(maxNeedBranch);
			}

			for(int whichSquad = 0 ; whichSquad < _state.EnemyGroup.Squads.size() ; ++whichSquad)
			{
				_state.EnemyGroup.Squads[whichSquad].Tick(minNeedBranch);
			}

			if(maxNeedBranch)
			{
				return Max(depth , alpha , beta);
			}

			if(minNeedBranch)
			{
				return Min(depth , alpha , beta);
			}
		}
	}
	int Evaluate()
	{
		++_evaluateCount;
		int score = 0;
		int livingAmount = 0;
		for(int whichUnit = 0 ; whichUnit < _state.MyGroup.Units.size() ; ++whichUnit)
		{
			if(_state.MyGroup.Units[whichUnit].Alive())
			{
				//score += _state.MyGroup.Units[whichUnit].Hp;
				score += sqrt(_state.MyGroup.Units[whichUnit].Hp);
				++livingAmount;
			}
		}
		//score += livingAmount * 50;

		livingAmount = 0;
		for(int whichUnit = 0 ; whichUnit < _state.EnemyGroup.Units.size() ; ++whichUnit)
		{
			//score -= _state.EnemyGroup.Units[whichUnit].Hp;
			score -= sqrt(_state.EnemyGroup.Units[whichUnit].Hp);
			++livingAmount;
		}
		//score -= livingAmount * 50;
		return score;
	}
};
